#ifndef RAY_TRACE_PRO_SHADER_H
#define RAY_TRACE_PRO_SHADER_H
#include "VectorMath.h"
#include "Basic/GxLibBasic.h"

namespace RayTracePro
{
	class Scene;
	class Ray;
	class ShadingNode;
	class DifferentialGeometry;
	class RayDifferential;
	class RayTraceSystem;
	class RenderThreadStorage;

	class ShadingNodeParameter
	{
	private:
		ShadingNode * Shader;
		GxLibMath::Vec4 Value;
	public:
		ShadingNodeParameter()
		{
			Shader = 0;
			Value.SetZero();
		}
		~ShadingNodeParameter();
		ShadingNodeParameter(const ShadingNodeParameter & sp)
		{
			operator = (sp);
		}
		ShadingNodeParameter & operator = (const ShadingNodeParameter & sp);
		inline bool Set(ShadingNode * shader)
		{
			Shader = shader;
			return true;
		}
		inline bool Set(const GxLibMath::Vec4 & value)
		{
			Shader = 0;
			Value = value;
			return true;
		}
		inline void Get(GxLibMath::Vec4 * result, DifferentialGeometry * dgeom, const Ray * ray, const RayDifferential * diff, Scene * scene, int depth);
		inline void GetSimplified(GxLibMath::Vec4 * result, DifferentialGeometry * dgeom, const Ray * ray, const RayDifferential * diff, Scene * scene, int depth);

	};


	class ShadingNode : public GxLibBasic::Object
	{
	private:
		int refCount;
	public:
		enum ShadingNodeReturnType
		{
			Float = 0, Float3 = 1, Float4 = 2
		};
		struct ParameterType
		{
			enum ParameterBaseType
			{
				ParameterTypeInt = 0, ParameterTypeFloat = 1, ParameterTypeFloat3 = 2, ParameterTypeFloat4 = 3, ParameterTypeString = 4, ParameterTypeEnumeration = 5
			} BaseType;
			int EnumValueCount;
			char * EnumValues[16];
			ParameterType()
			{
				BaseType = ParameterTypeInt;
				EnumValueCount = 0;
			}
			ParameterType(ParameterBaseType t)
			{
				BaseType = t;
				EnumValueCount = 0;
			}
		};
		ShadingNode()
		{
			refCount = 1;
		}
		void AddRef()
		{
			refCount ++;
		}
		virtual char* GetCategory() = 0;
		virtual ShadingNodeReturnType GetReturnType() = 0;
		virtual int GetParameterCount() = 0;
		virtual char* GetParameterName(int i) = 0;
		virtual void GetParameterType(ParameterType * rs, int i) = 0;
		virtual bool SetParameter(const wchar_t * param, const GxLibMath::Vec4 & value) = 0;
		virtual bool SetParameter(const wchar_t * param, ShadingNode * shader) = 0;
		virtual bool SetParameter(const wchar_t * param, const wchar_t * value) = 0;
		virtual bool GetParameter(const wchar_t * param, ShadingNodeParameter & rs) = 0;
		virtual bool GetParameter(const wchar_t * param, const wchar_t **value) = 0;
		virtual void Shade(GxLibMath::Vec4 * result, DifferentialGeometry * dgeom, const Ray * ray, const RayDifferential * rayDiff, Scene * scene, int depth) = 0;
		virtual void SimplifiedShade(GxLibMath::Vec4 * result, DifferentialGeometry * dgeom, const Ray * ray, const RayDifferential * rayDiff, Scene * scene, int depth) = 0;
		virtual void Init(RayTraceSystem * system) = 0;
		virtual void Free()
		{
			refCount --;
			if (!refCount)
				delete this;
		}
	};

	inline void ShadingNodeParameter::Get(GxLibMath::Vec4 * result, DifferentialGeometry * dgeom, const Ray * ray, const RayDifferential * diff, Scene * scene, int depth)
	{
		if (Shader)
			Shader->Shade(result, dgeom, ray, diff, scene, depth);
		else
			*result = Value;
	}
	inline void ShadingNodeParameter::GetSimplified(GxLibMath::Vec4 * result, DifferentialGeometry * dgeom, const Ray * ray, const RayDifferential * diff, Scene * scene, int depth)
	{
		if (Shader)
			Shader->SimplifiedShade(result, dgeom, ray, diff, scene, depth);
		else
			*result = Value;
	}
}

#endif